/* COPYRIGHT_CHUNFENG */
#include <unistd.h>
#include <errno.h>
#define USE_ERROR_LOG
#include "lcf_log.h"
#include <cf_buffer.h>

int CFBufferInit(CFBuffer* this, size_t unit)
{
    this->data = malloc(unit);
    if(!this->data){
        LCF_ERR("malloc failed on new data for new buffer\n");
        return -1;
    }
    this->unit = unit;
    this->len = 0;
    this->curr_size = unit;
    return 0;
}

int CFBufferExit(CFBuffer* this)
{
    free(this->data);
    this->unit = 0;
    this->len = 0;
    this->curr_size = 0;
    return 0;
}

CFBuffer* CFBufferNew(size_t unit)
{
    CFBuffer* ret;
    ret = (CFBuffer*)malloc(sizeof(CFBuffer));
    if(!ret){
        LCF_ERR("malloc failed on new buffer\n");
        return NULL;
    }
    if(CFBufferInit(ret, unit)){
        free(ret);
        return NULL;
    }
    return ret;
}

int CFBufferCp(CFBuffer* to, const void* source, size_t len)
{
    size_t needed_size = to->unit + len;
    if(to->curr_size < len || to->curr_size > needed_size){
        to->data = realloc(to->data, needed_size);
        if(!to->data){
            LCF_ERR("realloc() failed for buffer_cp\n");
            return -1;
        }
        to->curr_size = needed_size;
    }
    if(source)
    	memcpy(to->data, source, len);
    to->len = len;
    return 0;
}

int CFBufferRead(int fd, CFBuffer* to, BOOL async)
{
    int readed;
    
    while(1){
        if(to->curr_size == to->len){
            to->data = realloc(to->data, to->len+to->unit);
            to->curr_size = to->len+to->unit;
        }
        readed = read(fd, to->data+to->len, to->curr_size-to->len);
        if((async && readed==-1 && errno==EAGAIN) || readed == 0){ /* blocked or closed */
            return 0;
        }else if(readed == -1){ /* error */
            return -1;
        }
        to->len += readed;
    }
}

int CFBufferWrite(int fd, CFBuffer* from, BOOL async)
{
    int writed = 0;
    int writedOnce;

    while(1){
        if(writed == from->len){
            CFBufferCp(from, NULL, 0);
            return 0;
        }
        writedOnce = write(fd, from->data+writed, from->len-writed);
        if((async && writedOnce==-1 && (errno==EAGAIN || errno==EPIPE))){ /* blocked or closed */
            memcpy(from->data, from->data+writed, from->len-writed);
            from->len -= writed;
            return 0;
        }else if(writedOnce == -1){ /* error */
            memcpy(from->data, from->data+writed, from->len-writed);
            from->len -= writed;
            return -1;
        }else{
            writed += writedOnce;
        }
        
    }    
}

int CFBufferCat(CFBuffer* to, const void* source, size_t len)
{
    size_t new_len = len + to->len;
    size_t needed_size = to->unit + new_len;
    if(to->curr_size < new_len || to->curr_size > needed_size){
        to->data = realloc(to->data, needed_size);
        if(!to->data){
            LCF_ERR("realloc() failed for buffer_cat\n");
            return -1;
        }
        to->curr_size = needed_size;
    }
    if(source)
        memcpy(to->data + to->len, source, len);
    to->len = new_len;
    return 0;    
}

void* CFBufferGetPtr(CFBuffer* buf)
{
    return buf->data;
}

size_t CFBufferGetSize(CFBuffer* buf)
{
    return buf->len;
}

int CFBufferFree(CFBuffer* buf)
{
    CFBufferExit(buf);
    free(buf);
    return 0;
}

#if 0
int main(int argc, char** argv)
{
    CFBuffer* buf = CFBufferNew(0);
    CFBufferCp(buf, "123456789", 9);
    CFBufferCat(buf, "123456789", 10);
    CFBufferCp(buf, "abcd", 3);
    printf("length=%d\ndata: %s\n", CFBufferGetLen(buf), CFBufferGetData(buf));
    CFBufferFree(buf);
    return 0;
}
#endif
